Skip to content

feat(setup): make local daemon onboarding configurable#182

Merged
ian-pascoe merged 24 commits into
mainfrom
refactor/onboarding-experience
Jun 30, 2026
Merged

feat(setup): make local daemon onboarding configurable#182
ian-pascoe merged 24 commits into
mainfrom
refactor/onboarding-experience

Conversation

@ian-pascoe

@ian-pascoe ian-pascoe commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Local onboarding now routes agents through a managed Caplets daemon instead of asking every MCP client to run the full Caplets runtime, and the local-only path stays credential-free. The same daemon/HTTP serving surface can now be shaped with safe global serve defaults, so users can customize host, port, base path, auth, proxy trust, and public origins without letting project configs widen the server.

What changed

  • caplets setup creates or validates user config, installs or reuses a healthy local daemon, and only then mutates selected MCP/native integrations.
  • MCP setup uses the pinned add-mcp programmatic API, but Caplets owns the picker and writes each client as a thin caplets attach <local-daemon-url> command.
  • Local loopback daemon attach is handled as a credential-free local selection; remote/cloud setup remains explicit and keeps remote credentials in Caplets-owned stores.
  • OpenCode and Pi receive non-secret daemon defaults from setup while explicit plugin config, Pi settings, and runtime environment selectors still win.
  • Global user config now exposes optional serve defaults for the HTTP daemon/serve path, including multiple public origins; project serve config is ignored with warnings for safety.
  • Public docs, README, schemas, and troubleshooting now steer users toward daemon-first setup and document global-only serve defaults.

Validation

  • pnpm verify
  • Pre-push hook reran pnpm verify during git push -u origin HEAD; the full suite reported 137 test files and 1990 tests passing, benchmark docs up to date, and build completed.
  • Review fallback found a secondary publicOrigins credential-host gap after ce-code-review subagents timed out; fixed and covered by a regression in packages/core/test/serve-http.test.ts.

Compound Engineering

Summary by CodeRabbit

  • New Features

    • Added daemon-first setup for local agent integration, including automatic daemon startup/health checks and caplets attach-based client wiring.
    • Added support for daemon mode and native defaults for OpenCode and Pi.
    • Introduced user-level HTTP serve defaults, including public origins and restart-friendly settings.
  • Bug Fixes

    • Improved remote/local selection so loopback daemon setups are detected more reliably.
    • Tightened setup validation and clearer error messages for agent client configuration.
  • Documentation

    • Updated installation, setup, troubleshooting, and configuration guides to match the new workflow.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ian-pascoe, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 15 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5eca8493-fcf0-400d-8482-ddb7282338e3

📥 Commits

Reviewing files that changed from the base of the PR and between 6b3c985 and 6a2fe5e.

📒 Files selected for processing (5)
  • packages/core/src/daemon/client-url.ts
  • packages/core/src/daemon/index.ts
  • packages/core/src/project-binding/attach.ts
  • packages/core/src/remote/selection.ts
  • packages/core/test/remote-selection.test.ts
📝 Walkthrough

Walkthrough

Adds daemon-first setup, native daemon mode and defaults, add-mcp-based MCP client setup, and global HTTP serve defaults with matching config, routing, docs, and tests.

Changes

Daemon-first setup onboarding

Layer / File(s) Summary
Daemon mode and local attach selection
packages/core/src/daemon/index.ts, packages/core/src/remote/selection.ts, packages/core/src/project-binding/attach.ts, packages/core/src/native/options.ts, packages/core/src/native/service.ts, packages/core/src/native/user-settings.ts, packages/core/src/native.ts
Adds loopback daemon client URL derivation, local_daemon selection, native daemon mode, and native defaults persistence/re-exports.
Setup orchestration and MCP client wiring
packages/core/package.json, packages/core/src/cli/add-mcp-adapter.ts, packages/core/src/cli/completion.ts, packages/core/src/cli.ts, packages/core/src/cli/setup.ts
Adds the pinned add-mcp dependency, CLI --client plumbing, dynamic completion, phased setup orchestration, attach-based MCP config generation, and native defaults writes.
OpenCode and Pi daemon defaults
packages/opencode/src/index.ts, packages/pi/src/index.ts
OpenCode and Pi now accept daemon runtime options and load native defaults when explicit runtime selection is absent.
Daemon-first setup tests
packages/core/test/*, packages/opencode/test/*, packages/pi/test/*
Tests cover daemon URL derivation, daemon mode service wiring, add-mcp adapter behavior, phased setup execution, CLI client prompting/completion, and OpenCode/Pi daemon-default loading.
Daemon-first docs and plan
README.md, CONCEPTS.md, docs/native-integrations.md, apps/docs/src/content/docs/*, apps/landing/src/data/landing.ts, packages/opencode/README.md, packages/pi/README.md, .changeset/daemon-first-setup.md, docs/plans/2026-06-30-001-feat-daemon-first-setup-onboarding-plan.md
Docs, copy, changeset, and the onboarding plan describe daemon-first setup, attach-based MCP configs, and native daemon defaults.

Global HTTP serve defaults

Layer / File(s) Summary
Serve config and resolution
packages/core/src/config.ts, packages/core/src/serve/options.ts, packages/core/src/daemon/types.ts, packages/core/src/daemon/process.ts, packages/core/src/daemon/index.ts, schemas/caplets-config.schema.json
Adds top-level user serve defaults, resolves HTTP serve options with defaults and public origins, and persists daemon serve overrides.
Serve HTTP routing and daemon lifecycle
packages/core/src/serve/http.ts, packages/core/src/daemon/index.ts, packages/core/src/daemon/process.ts
Updates remote credential host derivation, DNS rebinding host handling, and daemon restart refresh behavior to use the new serve defaults.
Serve docs, schema, and tests
README.md, CONCEPTS.md, docs/architecture.md, apps/docs/src/content/docs/*, apps/landing/public/config.schema.json, scripts/check-public-docs.ts, scripts/generate-docs-reference.ts, docs/plans/2026-06-30-002-feat-global-serve-config-defaults-plan.md, .changeset/global-serve-config.md, packages/core/test/serve-*, packages/core/test/config.test.ts, packages/core/test/cli.test.ts, packages/core/test/setup-runner.test.ts
Docs, schema generation, lint checks, and tests are updated for global serve defaults, public origins, precedence, daemon restart behavior, and project-config restrictions.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • spiritledsoftware/caplets#133: This PR extends the daemon subsystem by adding daemon client URL derivation and persisted serve overrides on top of the daemon command surface.
  • spiritledsoftware/caplets#134: Both PRs modify remote selection and credential routing for loopback/self-hosted attach flows.
  • spiritledsoftware/caplets#62: This PR updates the HTTP serve/origin/auth handling layer that the earlier serve work introduced.

Poem

🐇 I hopped through the daemon door,
with attach prints and defaults galore.
Two burrows now hum, one local, one wide,
with serve and daemon both tucked inside.
My whiskers twitch: setup feels neat —
a loopback carrot and a config treat!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: configurable daemon-first setup for local onboarding.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/onboarding-experience

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR makes local setup use a managed Caplets daemon by default. The main changes are:

  • caplets setup prepares and health-checks the local daemon before writing integrations.
  • MCP clients are configured as thin caplets attach <local-daemon-url> commands.
  • OpenCode and Pi can use setup-written daemon defaults while explicit config still wins.
  • User-level serve defaults now cover HTTP host, port, path, auth, proxy, and public origins.
  • Docs, schemas, and tests were updated for the daemon-first flow.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.
  • The prior local-daemon and empty native-config paths now have direct fixes and test coverage.

Important Files Changed

Filename Overview
packages/opencode/src/index.ts Empty OpenCode config now falls through to setup-written daemon defaults.
packages/core/src/remote/selection.ts Loopback attach now requires a matching persisted daemon config, running daemon status, and healthy daemon response.
packages/pi/src/index.ts Pi now resolves daemon defaults after explicit options, settings, and runtime environment selectors.
packages/core/src/serve/http.ts Remote credential host identity now handles multiple configured public origins.

Reviews (5): Last reviewed commit: "fix(remote): trust persisted daemon befo..." | Re-trigger Greptile

Comment thread packages/opencode/src/index.ts Outdated
Comment thread packages/core/src/remote/selection.ts Outdated
@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/remote/selection.ts (1)

75-107: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Local-daemon fallback still fails when a stale self-hosted profile exists.

refreshSelfHostedProfileIfNeeded() runs before the loopback fallback, and the refresh callback can throw remoteLoginRequired() / refresh errors for expired local profiles. That means caplets attach http://127.0.0.1:... can still get blocked by old auth state instead of resolving as local_daemon. Please short-circuit loopback HTTP URLs before refresh, or downgrade self-hosted refresh failures for loopback targets into the credential-free daemon path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/remote/selection.ts` around lines 75 - 107, The remote
selection flow in selection.ts is refreshing self-hosted credentials before it
can apply the loopback/local-daemon fallback, so stale auth can block a
127.0.0.1 target. Update the logic around the localDaemonFallback check and
refreshSelfHostedProfileIfNeeded call in the remote selection path to
short-circuit loopback HTTP URLs into local_daemon handling before any
self-hosted refresh, or catch refreshSelfHostedCredentials/remoteLoginRequired
failures for loopback targets and fall back to localDaemonRemoteSelection
instead of failing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/docs/src/content/docs/index.mdx`:
- Around line 33-37: The Quick Start text conflicts with the daemon-first setup
flow by telling users to run `npx caplets setup` even though the generated
config expects a locally available `caplets` binary. Update the docs content in
the Quick Start/setup section so the first-run path installs `caplets` before
invoking `setup`, or explicitly scope `npx` usage only to foreground-only
commands. Use the `caplets setup` and `caplets attach` guidance in this page to
align the installation and configuration steps.

In `@apps/docs/src/content/docs/install.mdx`:
- Around line 95-106: The manual config example in install.mdx hard-codes the
default daemon URL, which can mislead users whose daemon runs on a different
host or port. Update the example in the install docs to use a placeholder like
<local-daemon-url> or add a clear note telling readers to replace the attach
target in the caplets MCP server config with their configured daemon URL.

In `@docs/native-integrations.md`:
- Line 21: Update the native integrations docs paragraph to include the
environment-variable layer in the precedence order, alongside explicit
integration config, Pi settings, and native defaults. Clarify in the same
section that runtime selectors such as CAPLETS_MODE and the daemon/remote URL
env vars override setup defaults, so readers know native-defaults.json is not
the final source when those env vars are present.

In `@packages/core/src/cli/completion.ts`:
- Around line 46-47: The completion helper is returning client ids that
`resolveSetupMcpClient()` cannot actually use, so shell completion is suggesting
invalid `--client` values. Update `setupMcpClientIds()` in `completion.ts` to
filter the results from `listSupportedAddMcpClients()` so it only includes
clients accepted by `setup`/`resolveSetupMcpClient()` (for example,
stdio-capable clients only), keeping the completion list aligned with the values
that will succeed.

In `@packages/core/src/cli/setup.ts`:
- Around line 508-511: The fallback in projectConfigPath currently derives the
project config from dirname(userConfigPath(env)), which points at the
user-config directory instead of the working tree. Update projectConfigPath and
its use in defaultEnsureUserConfig() so the fallback resolves the project config
from the current cwd/worktree (using the existing project config resolution
helpers) rather than from userConfigPath, keeping CAPLETS_PROJECT_CONFIG as the
override. Ensure the validation path matches what the CLI/native integrations
will actually load.
- Around line 657-667: The MCP client prompt in resolveSetupMcpClient currently
uses the raw detected list, but parseMcpClientPromptAnswer only accepts
stdio-capable clients. Filter detectClients() results to only entries with
supportsStdio before building the prompt and before using the default selection,
while keeping listSupportedClients().filter((client) => client.supportsStdio) as
the fallback; this ensures formatMcpClientPrompt, isShowAllMcpClientsAnswer, and
parseMcpClientPromptAnswer all operate on valid choices.

In `@packages/core/src/native/options.ts`:
- Around line 59-69: `hasNativeRuntimeSelectionEnv()` is treating
`CAPLETS_DAEMON_URL` as a selection signal, but
`resolveNativeCapletsServiceOptions()` still only switches to daemon behavior
when `CAPLETS_MODE` or `input.mode` is explicitly "daemon". Update the daemon
resolution path in `resolveNativeCapletsServiceOptions()` so `input.daemon?.url`
or `CAPLETS_DAEMON_URL` implies daemon mode whenever no explicit non-daemon mode
was requested, while preserving existing precedence for `input.mode` and
`CAPLETS_MODE`; use the existing `hasNativeRuntimeSelectionEnv` and
daemon-related option handling to locate the relevant branch.

In `@packages/core/src/native/user-settings.ts`:
- Around line 45-73: `readNativeDefaults()` and `isNativeDefaults()` currently
accept any string for `daemon.url`, which lets invalid values reach daemon
startup and fail later in `resolveNativeDaemonOptions()`. Add URL validation in
this reader (or enforce it when writing defaults) so only loopback HTTP URLs are
accepted. If `daemon.url` is missing or invalid, have `readNativeDefaults()`
emit the existing warning via `writeWarning` and return `undefined` instead of
returning the parsed object.

In `@packages/core/test/cli.test.ts`:
- Around line 4032-4034: The CLI test fixture is inheriting the caller’s full
environment through io.env, which can let local CAPLETS_* variables interfere
with daemon-first behavior. Update the runCli fixture setup in cli.test.ts to
pass a minimal environment or explicitly remove all Caplets-specific keys before
calling runCli, keeping only the variables required for the test and the
CAPLETS_CONFIG override.

In `@packages/core/test/setup-runner.test.ts`:
- Around line 273-303: The Codex no-op setup tests only assert that runCommand
is not called, but they also need to verify no MCP config mutation happens. In
the runSetup("codex", …) test cases in setup-runner.test.ts, add a spy/stub for
mcpOperations.upsertServer and assert it is never invoked alongside the existing
daemonCalled and commands checks. Use the runSetup entry point and the
upsertServer symbol so these tests fail if the integration phase regresses and
mutates MCP state.

In `@packages/pi/src/index.ts`:
- Around line 337-340: The daemon polling interval handling in index.ts should
reject values that core will not accept before they reach service creation.
Update the validation around daemon.pollIntervalMs in the parsing logic to
require an integer value of at least 1000 (while still rejecting non-numbers and
non-finite values) and leave invalid settings out of parsedDaemon so they are
ignored with a warning instead of causing core failure.
- Around line 398-404: The status-widget eligibility check in the remote-mode
helper needs to account for daemon mode selected through environment-driven
defaults, since serviceOptions may be empty and the existing options.mode checks
alone won’t enable it. Update the eligibility logic in the function that
evaluates options.mode and remote URL so daemon startup via env also returns
true, using the same daemon-related symbols already present in this branch.

---

Outside diff comments:
In `@packages/core/src/remote/selection.ts`:
- Around line 75-107: The remote selection flow in selection.ts is refreshing
self-hosted credentials before it can apply the loopback/local-daemon fallback,
so stale auth can block a 127.0.0.1 target. Update the logic around the
localDaemonFallback check and refreshSelfHostedProfileIfNeeded call in the
remote selection path to short-circuit loopback HTTP URLs into local_daemon
handling before any self-hosted refresh, or catch
refreshSelfHostedCredentials/remoteLoginRequired failures for loopback targets
and fall back to localDaemonRemoteSelection instead of failing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ce969d1b-2704-467b-9545-0cf466613e14

📥 Commits

Reviewing files that changed from the base of the PR and between 787194c and d48554c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (40)
  • .changeset/daemon-first-setup.md
  • CONCEPTS.md
  • README.md
  • apps/docs/src/content/docs/agent-integrations.mdx
  • apps/docs/src/content/docs/index.mdx
  • apps/docs/src/content/docs/install.mdx
  • apps/docs/src/content/docs/remote-attach.mdx
  • apps/docs/src/content/docs/troubleshooting.mdx
  • apps/landing/src/data/landing.ts
  • docs/native-integrations.md
  • docs/plans/2026-06-30-001-feat-daemon-first-setup-onboarding-plan.md
  • packages/core/package.json
  • packages/core/src/cli.ts
  • packages/core/src/cli/add-mcp-adapter.ts
  • packages/core/src/cli/completion.ts
  • packages/core/src/cli/setup.ts
  • packages/core/src/daemon/index.ts
  • packages/core/src/native.ts
  • packages/core/src/native/options.ts
  • packages/core/src/native/service.ts
  • packages/core/src/native/user-settings.ts
  • packages/core/src/project-binding/attach.ts
  • packages/core/src/remote/selection.ts
  • packages/core/test/add-mcp-adapter.test.ts
  • packages/core/test/agent-plugins.test.ts
  • packages/core/test/attach-service-wiring.test.ts
  • packages/core/test/cli-completion.test.ts
  • packages/core/test/cli.test.ts
  • packages/core/test/native-options.test.ts
  • packages/core/test/native.test.ts
  • packages/core/test/package-boundaries.test.ts
  • packages/core/test/remote-selection.test.ts
  • packages/core/test/serve-daemon.test.ts
  • packages/core/test/setup-runner.test.ts
  • packages/opencode/README.md
  • packages/opencode/src/index.ts
  • packages/opencode/test/opencode.test.ts
  • packages/pi/README.md
  • packages/pi/src/index.ts
  • packages/pi/test/pi.test.ts

Comment thread apps/docs/src/content/docs/index.mdx
Comment thread apps/docs/src/content/docs/install.mdx
Comment thread docs/native-integrations.md Outdated
Comment thread packages/core/src/cli/completion.ts Outdated
Comment thread packages/core/src/cli/setup.ts Outdated
Comment thread packages/core/src/native/user-settings.ts
Comment thread packages/core/test/cli.test.ts
Comment thread packages/core/test/setup-runner.test.ts
Comment thread packages/pi/src/index.ts
Comment thread packages/pi/src/index.ts
Comment thread packages/opencode/src/index.ts Outdated
Comment thread packages/core/src/remote/selection.ts
Comment thread packages/opencode/src/index.ts Outdated
Comment thread packages/core/src/remote/selection.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/core/src/cli/setup.ts (1)

462-476: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don't reject unsafe existing daemons before the repair path.

Line 462 throws as soon as a persisted daemon host is non-loopback, so caplets setup fails instead of reinstalling/restarting the safe local daemon. That breaks the daemon-first local recovery flow described in this PR for users who already have a remote or otherwise unsafe daemon configured.

Suggested fix
 async function defaultEnsureDaemon(context: SetupPhaseContext): Promise<SetupPhaseResult> {
   const operation = daemonOperationOptions(context.env);
   const status = await daemonStatus(operation);
-  if (status.config) assertCredentialFreeLocalSetupDaemonHost(status.config);
   if (status.installed && status.running && status.health?.ok && status.config) {
-    if (!isCredentialFreeLocalSetupDaemon(status.config)) {
+    if (
+      !isLoopbackHost(status.config.serve.host) ||
+      !isCredentialFreeLocalSetupDaemon(status.config)
+    ) {
       return await installCredentialFreeLocalSetupDaemon(operation);
     }
     return {
       phase: "daemon",
       label: "Reuse local Caplets daemon",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/cli/setup.ts` around lines 462 - 476, The setup flow in
setup.ts is rejecting persisted daemon hosts too early, which prevents the
repair path from reinstalling or restarting a safe local daemon. Update the
logic around the status/config handling in the setup operation so the
unsafe-host check does not short-circuit before
installCredentialFreeLocalSetupDaemon(operation) can run; only reuse the daemon
when isCredentialFreeLocalSetupDaemon(status.config) is true and the daemon is
healthy. Keep the existing reuse branch in the main success path, but allow
non-loopback or otherwise unsafe configs to fall through to the
reinstall/recovery path instead of calling
assertCredentialFreeLocalSetupDaemonHost(status.config) upfront.
packages/core/src/serve/http.ts (1)

157-165: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Honor publicOrigins in the trust-proxy preflight check.

This guard still rejects any remote_credentials + trustProxy config unless publicOrigin is set, even though the rest of the file now supports publicOrigins directly. A caller that passes only publicOrigins will fail at startup for no functional reason.

Suggested fix
   if (
     options.auth.type === "remote_credentials" &&
     options.trustProxy === true &&
-    options.publicOrigin === undefined
+    publicOriginsForOptions(options).length === 0
   ) {
     throw new CapletsError(
       "REQUEST_INVALID",
-      "Remote credential auth with --trust-proxy requires CAPLETS_SERVER_URL.",
+      "Remote credential auth with --trust-proxy requires a configured public origin.",
     );
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/serve/http.ts` around lines 157 - 165, The trust-proxy
preflight check in http.ts still only looks at options.publicOrigin, so
remote_credentials with trustProxy incorrectly fails even when
options.publicOrigins is provided. Update the guard around the CapletsError in
the HTTP startup path to accept either publicOrigin or publicOrigins as
satisfying the requirement, keeping the check aligned with the rest of the
file’s public origin handling.
🧹 Nitpick comments (1)
packages/core/test/setup-runner.test.ts (1)

976-980: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the daemonClientBaseUrl stub enforce loopback like production.

The real helper rejects non-loopback daemon hosts, but this stub always returns a URL. That weakens the regression signal in the non-loopback setup tests if the guard ever moves from assertCredentialFreeLocalSetupDaemonHost() into daemonClientBaseUrl().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/test/setup-runner.test.ts` around lines 976 - 980, The
`daemonClientBaseUrl` test stub currently bypasses the production loopback guard
by always returning a URL, so update it to mirror the real helper’s behavior by
validating the `config.serve.host` before constructing the URL. Use the existing
`daemonClientBaseUrl` stub in `setup-runner.test.ts` and make it reject
non-loopback hosts the same way the production helper does, so the non-loopback
setup tests still fail if the guard moves from
`assertCredentialFreeLocalSetupDaemonHost()` into `daemonClientBaseUrl()`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/daemon/index.ts`:
- Around line 635-642: The early persisted-config guard in the daemon flow is
too broad and blocks `caplets daemon stop` when the config file is missing.
Update the logic in the daemon entry path so the `readDaemonConfig(paths)`
failure only throws for actions that need config (such as start/restart), while
`stop` can continue and call `DaemonManager.stop` without requiring `persisted`.
Keep the action check near `refreshDaemonServeConfig`/`config` selection so the
behavior for stop remains independent of the config file.

In `@packages/core/src/daemon/process.ts`:
- Around line 32-34: The `resolveServeOptions()` path is dropping the
`preserveUnauthenticatedAuth` sentinel too early, so `defaults` can still
override legacy credential-free daemons. Update the `process.ts` resolver logic
around `serveRaw`/`resolveServeOptions()` to detect
`preserveUnauthenticatedAuth` and force the unauthenticated HTTP auth behavior
before applying global defaults, rather than deleting the flag and continuing
with the normal merge. Make sure the fix preserves existing local daemon auth
state across restarts when `serve.allowUnauthenticatedHttp` is false, and keep
the behavior localized to the `resolveServeOptions()` / `mergeServeOverrides()`
flow.

In `@packages/core/src/serve/options.ts`:
- Around line 92-93: When CAPLETS_SERVER_URL is present, the publicOrigins logic
in options.ts is dropping any configured secondary origins by replacing
defaults.publicOrigins with only serverUrl.origin. Update the publicOrigins
selection so the env-provided origin is included without discarding
defaults.publicOrigins, preserving the existing multi-origin list used by the
attach/auth flow. Use the publicOrigins and publicOrigin handling in the serve
options builder to locate the fix.

In `@schemas/caplets-config.schema.json`:
- Around line 72-79: Tighten the `publicOrigins` schema so it matches
`parseConfig()` runtime validation: update `schemas/caplets-config.schema.json`
to constrain each entry to an origin-only HTTP(S) URL with no path, query,
fragment, or userinfo, and apply the same rule in
`apps/landing/public/config.schema.json` so both published schemas stay aligned
with the config parser’s contract.

---

Outside diff comments:
In `@packages/core/src/cli/setup.ts`:
- Around line 462-476: The setup flow in setup.ts is rejecting persisted daemon
hosts too early, which prevents the repair path from reinstalling or restarting
a safe local daemon. Update the logic around the status/config handling in the
setup operation so the unsafe-host check does not short-circuit before
installCredentialFreeLocalSetupDaemon(operation) can run; only reuse the daemon
when isCredentialFreeLocalSetupDaemon(status.config) is true and the daemon is
healthy. Keep the existing reuse branch in the main success path, but allow
non-loopback or otherwise unsafe configs to fall through to the
reinstall/recovery path instead of calling
assertCredentialFreeLocalSetupDaemonHost(status.config) upfront.

In `@packages/core/src/serve/http.ts`:
- Around line 157-165: The trust-proxy preflight check in http.ts still only
looks at options.publicOrigin, so remote_credentials with trustProxy incorrectly
fails even when options.publicOrigins is provided. Update the guard around the
CapletsError in the HTTP startup path to accept either publicOrigin or
publicOrigins as satisfying the requirement, keeping the check aligned with the
rest of the file’s public origin handling.

---

Nitpick comments:
In `@packages/core/test/setup-runner.test.ts`:
- Around line 976-980: The `daemonClientBaseUrl` test stub currently bypasses
the production loopback guard by always returning a URL, so update it to mirror
the real helper’s behavior by validating the `config.serve.host` before
constructing the URL. Use the existing `daemonClientBaseUrl` stub in
`setup-runner.test.ts` and make it reject non-loopback hosts the same way the
production helper does, so the non-loopback setup tests still fail if the guard
moves from `assertCredentialFreeLocalSetupDaemonHost()` into
`daemonClientBaseUrl()`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8b08bd78-c60d-41fe-a1c2-93594d11f037

📥 Commits

Reviewing files that changed from the base of the PR and between d48554c and f5607ec.

📒 Files selected for processing (27)
  • .changeset/global-serve-config.md
  • CONCEPTS.md
  • README.md
  • apps/docs/src/content/docs/configuration.mdx
  • apps/docs/src/content/docs/install.mdx
  • apps/docs/src/content/docs/reference/config.mdx
  • apps/docs/src/content/docs/troubleshooting.mdx
  • apps/landing/public/config.schema.json
  • docs/architecture.md
  • docs/plans/2026-06-30-002-feat-global-serve-config-defaults-plan.md
  • packages/core/src/cli.ts
  • packages/core/src/cli/setup.ts
  • packages/core/src/config.ts
  • packages/core/src/daemon/index.ts
  • packages/core/src/daemon/process.ts
  • packages/core/src/daemon/types.ts
  • packages/core/src/serve/http.ts
  • packages/core/src/serve/options.ts
  • packages/core/test/cli.test.ts
  • packages/core/test/config.test.ts
  • packages/core/test/serve-daemon.test.ts
  • packages/core/test/serve-http.test.ts
  • packages/core/test/serve-options.test.ts
  • packages/core/test/setup-runner.test.ts
  • schemas/caplets-config.schema.json
  • scripts/check-public-docs.ts
  • scripts/generate-docs-reference.ts
✅ Files skipped from review due to trivial changes (7)
  • .changeset/global-serve-config.md
  • docs/architecture.md
  • CONCEPTS.md
  • apps/docs/src/content/docs/configuration.mdx
  • apps/docs/src/content/docs/troubleshooting.mdx
  • apps/docs/src/content/docs/reference/config.mdx
  • apps/docs/src/content/docs/install.mdx
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • packages/core/test/cli.test.ts

Comment thread packages/core/src/daemon/index.ts Outdated
Comment thread packages/core/src/daemon/process.ts
Comment thread packages/core/src/serve/options.ts Outdated
Comment thread schemas/caplets-config.schema.json
@ian-pascoe ian-pascoe changed the title feat(setup): make local onboarding daemon-first feat(setup): make local daemon onboarding configurable Jun 30, 2026
Preserve local-only daemon setup without remote auth friction while tightening project config validation, native default precedence, local daemon discovery, and setup/docs contracts from PR review.
Comment thread packages/core/src/remote/selection.ts
Require credential-free local-daemon attach fallback to match the setup-written daemon config and pass native running plus health checks before bypassing remote credentials. Spoofed loopback services now fall back to remote credential requirements.
@ian-pascoe ian-pascoe merged commit 73cc952 into main Jun 30, 2026
7 checks passed
@ian-pascoe ian-pascoe deleted the refactor/onboarding-experience branch June 30, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant